﻿using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace UtilZ.Dotnet.Ex.Base
{
#pragma warning disable CA1416

    /// <summary>
    /// 安全类扩展
    /// </summary>
    public static class SecurityEx
    {
        #region 计算hash值

        /// <summary>
        /// 计算hash值
        /// </summary>
        /// <param name="data">要计算hash值的数据</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>hash值</returns>
        /// <exception cref="ArgumentNullException">数据空引用异常</exception>
        public static byte[] ComputeHash(byte[] data, HashAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (HashAlgorithm entry = CreateHashEntry(algorithmType))
            {
                //计算指定Stream 对象的哈希值
                return entry.ComputeHash(data);
            }
        }

        /// <summary>
        /// 计算hash值
        /// </summary>
        /// <param name="data">要计算hash值的数据</param>
        /// <param name="offset">要计算hash值的数据起始位置</param>
        /// <param name="count">要计算hash值的数据长度</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>hash值</returns>
        /// <exception cref="ArgumentNullException">数据空引用异常</exception>
        public static byte[] ComputeHash(byte[] data, int offset, int count, HashAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (HashAlgorithm entry = CreateHashEntry(algorithmType))
            {
                //计算指定Stream 对象的哈希值
                return entry.ComputeHash(data, offset, count);
            }
        }

        /// <summary>
        /// 计算hash值
        /// </summary>
        /// <param name="data">要计算hash值的数据</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>hash值</returns>
        /// <exception cref="ArgumentNullException">数据空引用异常</exception>
        public static byte[] ComputeHash(Stream data, HashAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (HashAlgorithm entry = CreateHashEntry(algorithmType))
            {
                //计算指定Stream 对象的哈希值
                return entry.ComputeHash(data);
            }
        }

        /// <summary>
        /// 创建Hash实例
        /// </summary>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>Hash实例</returns>
        public static HashAlgorithm CreateHashEntry(HashAlgorithmType algorithmType)
        {
            HashAlgorithm entry;
            switch (algorithmType)
            {
                case HashAlgorithmType.MD5:
                    entry = MD5.Create();
                    break;
                case HashAlgorithmType.SHA1:
                    entry = SHA1.Create();
                    break;
                case HashAlgorithmType.SHA256:
                    entry = SHA256.Create();
                    break;
                case HashAlgorithmType.SHA384:
                    entry = SHA384.Create();
                    break;
                case HashAlgorithmType.SHA512:
                    entry = SHA512.Create();
                    break;

                //-------------------------------------------------
#if CORE

#else
                case HashAlgorithmType.HMAC:
                    entry = HMAC.Create();
                    break;
                case HashAlgorithmType.KeyedHashAlgorithm:
                    entry = KeyedHashAlgorithm.Create();
                    break;
                case HashAlgorithmType.RIPEMD160:
                    entry = RIPEMD160.Create();
                    break;
#endif
                default:
                    throw new NotImplementedException($"未实现的Hash算法实例创建类型:{algorithmType.ToString()}");
            }

            return entry;
        }

        #endregion



        #region 对称加解密

        /// <summary>
        /// CryptoStream扩展方法读取数据,返回读取到数据长度
        /// </summary>
        /// <param name="stream">CryptoStream</param>
        /// <param name="buffer">存放读取到的结果buffer</param>
        /// <param name="offset">存放数据起始位置</param>
        /// <param name="count">最多存放数据个数</param>
        /// <returns>读取到数据长度</returns>
        public static int ReadEx(this CryptoStream stream, byte[] buffer, int offset, int count)
        {
            //int length = cs.Read(result, 0, result.Length);//注:一次性读取可能读取不完,需要多次读取
            int length = 0, tempLen;
            while (true)
            {
                tempLen = stream.Read(buffer, offset + length, count - length);
                if (tempLen > 0)
                {
                    length += tempLen;
                }
                else
                {
                    break;
                }
            }
            return length;
        }

#if CORE

        /// <summary>
        /// CryptoStream扩展方法读取数据,返回读取到数据长度
        /// </summary>
        /// <param name="stream">CryptoStream</param>
        /// <param name="destination">存放读取到的结果buffer</param>
        /// <returns>读取到数据长度</returns>
        public static int ReadEx(this CryptoStream stream, Span<byte> destination)
        {
            //int length = cs.Read(result, 0, result.Length);//注:一次性读取可能读取不完,需要多次读取
            int length = 0, tempLen;
            while (true)
            {
                tempLen = stream.Read(destination.Slice(length));
                if (tempLen > 0)
                {
                    length += tempLen;
                }
                else
                {
                    break;
                }
            }

            return length;
        }

#endif



        private static void CheckSymmetricKeyAndIV(SymmetricAlgorithm entry, byte[] key, byte[] iv)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (iv == null)
            {
                throw new ArgumentNullException(nameof(iv));
            }

            bool keyLengthValid = false;
            int tempKeySize, expectLen = key.Length * 8;
            foreach (KeySizes keySize in entry.LegalKeySizes)
            {
                tempKeySize = keySize.MinSize;
                while (tempKeySize <= keySize.MaxSize)
                {
                    if (tempKeySize == expectLen)
                    {
                        keyLengthValid = true;
                        break;
                    }
                    tempKeySize += keySize.SkipSize;
                }

                if (keyLengthValid)
                {
                    break;
                }
            }

            if (keyLengthValid)
            {
                if (entry.BlockSize / 8 != iv.Length)
                {
                    throw new ArgumentOutOfRangeException($"IV长度{iv.Length}无效,IV长度必须为{entry.BlockSize / 8}字节.");
                }
            }
            else
            {
                throw new ArgumentOutOfRangeException($"key长度{key.Length}无效,key长度参见SymmetricAlgorithm.LegalKeySizes");
            }

        }

        /// <summary>
        /// 使用对称加密算法加密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <param name="key">对称算法密钥,密钥长度要求:(AES:16/24/32字节),(DES:7字节),(3DES:14/21字节)</param>
        /// <param name="iv">对称算法的初始化向量</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>加密后的数据</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static byte[] SymmetricEncrypt(byte[] data, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
            {
                CheckSymmetricKeyAndIV(entry, key, iv);

                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, entry.CreateEncryptor(key, iv), CryptoStreamMode.Write))
                    {
                        cs.Write(data, 0, data.Length);
                        cs.FlushFinalBlock();
                        return ms.ToArray();
                    }
                }
            }
        }

        /// <summary>
        /// 使用对称加密算法解密数据
        /// </summary>
        /// <param name="data">要解密的数据</param>
        /// <param name="key">对称算法密钥</param>
        /// <param name="iv">对称算法的初始化向量</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>解密后的数据</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static byte[] SymmetricDecrypt(byte[] data, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
            {
                CheckSymmetricKeyAndIV(entry, key, iv);

                using (var ms = new MemoryStream(data))
                {
                    using (var cs = new CryptoStream(ms, entry.CreateDecryptor(key, iv), CryptoStreamMode.Read))
                    {
                        byte[] result = new byte[data.Length];
                        int length = cs.ReadEx(result, 0, result.Length);
                        return result.Take(length).ToArray();
                    }
                }
            }
        }

#if CORE

        /// <summary>
        /// 使用对称加密算法加密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <param name="destination">加密后的数据</param>
        /// <param name="destinationIndex">加密后的数据起始位置</param>
        /// <param name="destinationCount">加密后的数据可用长度</param>
        /// <param name="key">对称算法密钥,密钥长度要求:(AES:16/24/32字节),(DES:7字节),(3DES:14/21字节)</param>
        /// <param name="iv">对称算法的初始化向量</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>加密后的数据长度</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static int SymmetricEncrypt(ReadOnlySpan<byte> data, byte[] destination, int destinationIndex, int destinationCount, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
            {
                CheckSymmetricKeyAndIV(entry, key, iv);

                using (MemoryStream ms = new MemoryStream(destination, destinationIndex, destinationCount))
                {
                    using (CryptoStream cs = new CryptoStream(ms, entry.CreateEncryptor(key, iv), CryptoStreamMode.Write))
                    {
                        cs.Write(data);
                        cs.FlushFinalBlock();
                        return (int)ms.Position;
                    }
                }
            }
        }

        /// <summary>
        /// 使用对称加密算法解密数据
        /// </summary>
        /// <param name="data">要解密的数据</param>
        /// <param name="index">要解密的数据起始位置</param>
        /// <param name="count">要解密的数据长度</param>
        /// <param name="destination">解密后的数据</param>
        /// <param name="key">对称算法密钥</param>
        /// <param name="iv">对称算法的初始化向量</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>解密后的数据长度</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static int SymmetricDecrypt(byte[] data, int index, int count, Span<byte> destination, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
            {
                CheckSymmetricKeyAndIV(entry, key, iv);

                using (var ms = new MemoryStream(data, index, count))
                {
                    using (var cs = new CryptoStream(ms, entry.CreateDecryptor(key, iv), CryptoStreamMode.Read))
                    {
                        return cs.ReadEx(destination);
                    }
                }
            }
        }

#endif

        /// <summary>
        /// 使用对称加密算法加密数据
        /// </summary>
        /// <param name="text">要加密的数据</param>
        /// <param name="key">对称算法密钥,密钥长度要求:(AES:16/24/32字节),(DES:7字节),(3DES:14/21字节)</param>
        /// <param name="iv">对称算法的初始化向量</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>加密后的数据</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static byte[] SymmetricEncryptText(string text, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            if (text == null)
            {
                throw new ArgumentNullException(nameof(text));
            }

            using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
            {
                CheckSymmetricKeyAndIV(entry, key, iv);

                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, entry.CreateEncryptor(key, iv), CryptoStreamMode.Write))
                    {
                        using (StreamWriter sw = new StreamWriter(cs))
                        {
                            //Write all data to the stream.
                            sw.Write(text);
                        }
                        return ms.ToArray();
                    }
                }
            }
        }

        /// <summary>
        /// 使用对称加密算法解密数据
        /// </summary>
        /// <param name="data">要解密的数据</param>
        /// <param name="key">对称算法密钥</param>
        /// <param name="iv">对称算法的初始化向量</param>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>解密后的数据</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static string SymmetricDecryptText(byte[] data, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
            {
                CheckSymmetricKeyAndIV(entry, key, iv);

                using (var ms = new MemoryStream(data))
                {
                    using (var cs = new CryptoStream(ms, entry.CreateDecryptor(key, iv), CryptoStreamMode.Read))
                    {
                        using (StreamReader sr = new StreamReader(cs))
                        {
                            return sr.ReadToEnd();
                        }
                    }
                }
            }
        }


        /// <summary>
        /// 对称加密文件
        /// </summary>
        /// <param name="inputFilePath"></param>
        /// <param name="outputFilePath"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <param name="algorithmType"></param>
        public static void SymmetricEncryptFile(string inputFilePath, string outputFilePath, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    if (fout.Length > 0)
                    {
                        fout.SetLength(fout.Length);
                    }

                    //Create variables to help with read and write.
                    byte[] buffer = new byte[100]; //This is intermediate storage for the encryption.
                    long rdlen = 0;              //This is the total number of bytes written.
                    long totlen = fin.Length;    //This is the total length of the input file.
                    int len;                     //This is the number of bytes to be written at a time.

                    using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
                    {
                        using (var encStream = new CryptoStream(fout, entry.CreateEncryptor(key, iv), CryptoStreamMode.Write))
                        {
                            //Read from the input file, then encrypt and write to the output file.
                            while (rdlen < totlen)
                            {
                                len = fin.Read(buffer, 0, 100);
                                if (len <= 0)
                                {
                                    break;
                                }

                                encStream.Write(buffer, 0, len);
                                rdlen = rdlen + len;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 对称解密文件
        /// </summary>
        /// <param name="inputFilePath"></param>
        /// <param name="outputFilePath"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <param name="algorithmType"></param>
        public static void SymmetricDecryptFile(string inputFilePath, string outputFilePath, byte[] key, byte[] iv, SymmetricAlgorithmType algorithmType)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    if (fout.Length > 0)
                    {
                        fout.SetLength(fout.Length);
                    }

                    //Create variables to help with read and write.
                    byte[] buffer = new byte[100]; //This is intermediate storage for the encryption.
                    long rdlen = 0;              //This is the total number of bytes written.
                    long totlen = fin.Length;    //This is the total length of the input file.
                    int len;                     //This is the number of bytes to be written at a time.

                    using (SymmetricAlgorithm entry = CreateSymmetricEntry(algorithmType))
                    {
                        CheckSymmetricKeyAndIV(entry, key, iv);
                        using (var encStream = new CryptoStream(fout, entry.CreateDecryptor(key, iv), CryptoStreamMode.Write))
                        {
                            //Read from the input file, then encrypt and write to the output file.
                            while (rdlen < totlen)
                            {
                                len = fin.Read(buffer, 0, buffer.Length);
                                if (len <= 0)
                                {
                                    break;
                                }

                                encStream.Write(buffer, 0, len);
                                rdlen = rdlen + len;
                            }
                        }
                    }
                }
            }
        }



        /// <summary>
        /// 创建对称加密实例
        /// </summary>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>对称加密实例</returns>
        /// <exception cref="NotImplementedException"></exception>
        public static SymmetricAlgorithm CreateSymmetricEntry(SymmetricAlgorithmType algorithmType)
        {
            SymmetricAlgorithm entry;
            switch (algorithmType)
            {
                case SymmetricAlgorithmType.Aes:
                    entry = Aes.Create();
                    break;
                case SymmetricAlgorithmType.DES:
                    entry = DES.Create();
                    break;
                case SymmetricAlgorithmType.RC2:
                    entry = RC2.Create();
                    break;
                case SymmetricAlgorithmType.TripleDES:
                    entry = TripleDES.Create();
                    break;

#if CORE


#else
                case SymmetricAlgorithmType.Rijndael:
                    entry = Rijndael.Create();
                    break;
#endif

                default:
                    throw new NotImplementedException($"未实现的对称加密算法类型实例创建类型:{algorithmType.ToString()}");
            }

            return entry;
        }

        #endregion


        #region 非对称加密

        /// <summary>
        /// 生成非对称加密密钥对
        /// </summary>
        /// <param name="dwKeySize">密钥大小,以位为单位,比如:2048</param>
        /// <returns>密钥对</returns>
        public static RSAKeys RSAKeys(int dwKeySize)
        {
            using (var rsa = new RSACryptoServiceProvider(dwKeySize))
            {
                string publicKey = rsa.ToXmlString(false);
                string privateKey = rsa.ToXmlString(true);
                return new RSAKeys(publicKey, privateKey);
            }
        }

        /// <summary>
        /// 创建非对称加密对象
        /// </summary>
        /// <param name="algorithmType">算法类型</param>
        /// <returns>非对称加密对象</returns>
        public static RSA CreateRSAEntry(RSAAlgorithmType algorithmType)
        {
            switch (algorithmType)
            {
                case RSAAlgorithmType.RSACryptoServiceProvider:
                    return new RSACryptoServiceProvider();
#if !NET4_0
                case RSAAlgorithmType.RSACng:
                    if (EnvironmentEx.OS == OSPlatformID.Win)
                    {
                        return new RSACng();
                    }
                    else
                    {
                        throw new NotSupportedException($"算法\"{algorithmType.ToString()}\"只支持windowxs平台");
                    }
#endif
#if CORE
                case RSAAlgorithmType.RSABCrypt:
                    return RSA.Create();
                //case RSAAlgorithmType.RSAOpenSsl:
                //    return new RSAOpenSsl();
#endif
                case RSAAlgorithmType.Default:
                default:
                    return RSA.Create();
            }
        }

#if NET4_0

        /// <summary>
        /// 使用对称加密算法加密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <param name="publicKey">公钥</param>
        /// <returns>加密后的数据</returns>
        /// <exception cref="ArgumentNullException">参数为null或空异常</exception>
        public static byte[] RSAEncrypt(byte[] data, string publicKey)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (string.IsNullOrEmpty(publicKey))
            {
                throw new ArgumentNullException(nameof(publicKey));
            }

            using (RSA entry = RSA.Create())
            {
                entry.FromXmlString(publicKey);
                return entry.EncryptValue(data);
            }
        }

        /// <summary>
        /// 使用对称加密算法解密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <param name="privateKey">私钥</param>
        /// <returns>加密后的数据</returns>
        /// <exception cref="ArgumentNullException">参数为null或空异常</exception>
        public static byte[] RSADecrypt(byte[] data, string privateKey)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (string.IsNullOrEmpty(privateKey))
            {
                throw new ArgumentNullException(nameof(privateKey));
            }

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.FromXmlString(privateKey);
                return rsa.DecryptValue(data);
            }
        }

#else

        /// <summary>
        /// 使用对称加密算法加密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <param name="publicKey">公钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        /// <returns>加密后的数据</returns>
        /// <exception cref="ArgumentNullException">参数为null或空异常</exception>
        public static byte[] RSAEncrypt(byte[] data, string publicKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (string.IsNullOrEmpty(publicKey))
            {
                throw new ArgumentNullException(nameof(publicKey));
            }

            using (RSA entry = CreateRSAEntry(algorithmType))
            {
                entry.FromXmlString(publicKey);
                return entry.Encrypt(data, padding);
            }
        }

        /// <summary>
        /// 使用对称加密算法解密数据
        /// </summary>
        /// <param name="data">要解密的数据</param>
        /// <param name="privateKey">私钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        /// <returns>加密后的数据</returns>
        /// <exception cref="ArgumentNullException">参数为null或空异常</exception>
        public static byte[] RSADecrypt(byte[] data, string privateKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (string.IsNullOrEmpty(privateKey))
            {
                throw new ArgumentNullException(nameof(privateKey));
            }

            using (var rsa = CreateRSAEntry(algorithmType))
            {
                rsa.FromXmlString(privateKey);
                return rsa.Decrypt(data, padding);
            }
        }

#if CORE

        /// <summary>
        /// 使用对称加密算法加密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <param name="destination">加密结果数据</param>
        /// <param name="publicKey">公钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        /// <returns>加密后的数据长度</returns>
        /// <exception cref="ArgumentNullException">参数为null或空异常</exception>
        public static int RSAEncrypt(ReadOnlySpan<byte> data, Span<byte> destination, string publicKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (string.IsNullOrEmpty(publicKey))
            {
                throw new ArgumentNullException(nameof(publicKey));
            }

            using (RSA entry = CreateRSAEntry(algorithmType))
            {
                entry.FromXmlString(publicKey);
                return entry.Encrypt(data, destination, padding);
            }
        }

        /// <summary>
        /// 使用对称加密算法解密数据
        /// </summary>
        /// <param name="data">要解密的数据</param>
        /// <param name="destination">加密结果数据</param>
        /// <param name="privateKey">私钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        /// <returns>加密后的数据长度</returns>
        /// <exception cref="ArgumentNullException">参数为null或空异常</exception>
        public static int RSADecrypt(ReadOnlySpan<byte> data, Span<byte> destination, string privateKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (string.IsNullOrEmpty(privateKey))
            {
                throw new ArgumentNullException(nameof(privateKey));
            }

            using (var rsa = CreateRSAEntry(algorithmType))
            {
                rsa.FromXmlString(privateKey);
                return rsa.Decrypt(data, destination, padding);
            }
        }

        /// <summary>
        /// 非对称加密文件
        /// </summary>
        /// <param name="inputFilePath">输入文件路径</param>
        /// <param name="outputFilePath">输出文件路径</param>
        /// <param name="publicKey">公钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        public static void RSAEncryptFile(string inputFilePath, string outputFilePath, string publicKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    if (fout.Length > 0)
                    {
                        fout.SetLength(fout.Length);
                    }

                    using (var rsa = CreateRSAEntry(algorithmType))
                    {
                        rsa.FromXmlString(publicKey);

                        //Create variables to help with read and write.
                        Span<byte> buffer = new Span<byte>(new byte[100]); //This is intermediate storage for the encryption.
                        Span<byte> destination = new Span<byte>(new byte[256]);
                        int length;

                        while (fin.Position < fin.Length)
                        {
                            length = fin.Read(buffer);
                            if (length <= 0)
                            {
                                break;
                            }

                            if (length < buffer.Length)
                            {
                                buffer = buffer.Slice(0, length);
                            }

                            length = rsa.Encrypt(buffer, destination, padding);
                            if (length < destination.Length)
                            {
                                fout.Write(destination.Slice(0, length));
                            }
                            else
                            {
                                fout.Write(destination);
                            }
                        }

                    }
                }
            }
        }

        /// <summary>
        /// 非对称解密文件
        /// </summary>
        /// <param name="inputFilePath">输入文件路径</param>
        /// <param name="outputFilePath">输出文件路径</param>
        /// <param name="privateKey">私钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        public static void RSADecryptFile(string inputFilePath, string outputFilePath, string privateKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    if (fout.Length > 0)
                    {
                        fout.SetLength(fout.Length);
                    }

                    Span<byte> buffer = new Span<byte>(new byte[256]); //This is intermediate storage for the encryption.
                    Span<byte> destination = new Span<byte>(new byte[100]);
                    int length;

                    using (var rsa = CreateRSAEntry(algorithmType))
                    {
                        rsa.FromXmlString(privateKey);
                        //Read from the input file, then encrypt and write to the output file.
                        while (fin.Position < fin.Length)
                        {
                            length = fin.Read(buffer);
                            if (length <= 0)
                            {
                                break;
                            }

                            if (length < buffer.Length)
                            {
                                buffer = buffer.Slice(0, length);
                            }

                            length = rsa.Decrypt(buffer, destination, padding);
                            if (length < destination.Length)
                            {
                                fout.Write(destination.Slice(0, length));
                            }
                            else
                            {
                                fout.Write(destination);
                            }
                        }
                    }
                }
            }
        }

#else

        /// <summary>
        /// 非对称加密文件
        /// </summary>
        /// <param name="inputFilePath">输入文件路径</param>
        /// <param name="outputFilePath">输出文件路径</param>
        /// <param name="publicKey">公钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        public static void RSAEncryptFile(string inputFilePath, string outputFilePath, string publicKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    if (fout.Length > 0)
                    {
                        fout.SetLength(fout.Length);
                    }

                    using (var rsa = CreateRSAEntry(algorithmType))
                    {
                        rsa.FromXmlString(publicKey);

                        //Create variables to help with read and write.
                        byte[] buffer = new byte[100]; //This is intermediate storage for the encryption.
                        byte[] destination;
                        int length;                     //This is the number of bytes to be written at a time.

                        while (fin.Position < fin.Length)
                        {
                            length = fin.Read(buffer, 0, buffer.Length);
                            if (length <= 0)
                            {
                                break;
                            }

                            if (length < buffer.Length)
                            {
                                buffer = buffer.Take(length).ToArray();
                            }

                            destination = rsa.Encrypt(buffer, padding);
                            fout.Write(destination);
                        }

                    }
                }
            }
        }

        /// <summary>
        /// 非对称解密文件
        /// </summary>
        /// <param name="inputFilePath">输入文件路径</param>
        /// <param name="outputFilePath">输出文件路径</param>
        /// <param name="privateKey">私钥</param>
        /// <param name="algorithmType">算法类型</param>
        /// <param name="padding">填充方式</param>
        public static void RSADecryptFile(string inputFilePath, string outputFilePath, string privateKey, RSAAlgorithmType algorithmType, RSAEncryptionPadding padding)
        {
            //Create the file streams to handle the input and output files.
            using (var fin = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
            {
                using (var fout = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    if (fout.Length > 0)
                    {
                        fout.SetLength(fout.Length);
                    }

                    //Create variables to help with read and write.
                    byte[] buffer = new byte[256]; //This is intermediate storage for the encryption.
                    int length;                     //This is the number of bytes to be written at a time.
                    byte[] destination;

                    using (var rsa = CreateRSAEntry(algorithmType))
                    {
                        rsa.FromXmlString(privateKey);
                        //Read from the input file, then encrypt and write to the output file.
                        while (fin.Position < fin.Length)
                        {
                            length = fin.Read(buffer, 0, buffer.Length);
                            if (length <= 0)
                            {
                                break;
                            }

                            if (length < buffer.Length)
                            {
                                buffer = buffer.Take(length).ToArray();
                            }

                            destination = rsa.Decrypt(buffer, padding);
                            fout.Write(destination);
                        }
                    }
                }
            }
        }

#endif


#endif

        #endregion


        #region yf加密

        /// <summary>
        /// 移位加密数据
        /// </summary>
        /// <param name="data">要加密的数据</param>
        /// <returns>加密后的数据长度</returns>
        public static byte[] MoveBitEncrypt(byte[] data)
        {
            //(A ~ Z)，算法:sourceByte => firstByte = (byte 右移 4) + 65 和 lastByte = (byte&(byte)15) + 65

            // 创建临时缓冲
            byte[] result = new byte[data.Length * 2];
            for (int i = 0; i < data.Length; i++)
            {
                byte b = data[i];
                // 编码sourceByte
                result[i * 2] = (byte)((b >> 4) + 65);
                result[i * 2 + 1] = (byte)((b & (byte)15) + 65);
            }

            return result;
        }

        /// <summary>
        /// 移位解密数据
        /// </summary>
        /// <param name="data">要解密的数据</param>
        /// <returns>解密后的数据</returns>
        public static byte[] MoveBitDecrypt(byte[] data)
        {
            if (data.Length % 2 != 0)
            {
                throw new Exception("buffer length error");
            }

            byte[] result = new byte[data.Length / 2];
            for (int i = 0; i < data.Length; i = i + 2)
            {
                result[i / 2] = (byte)(((data[i] - 65) << 4) + data[i + 1] - 65);
            }
            return result;
        }




        private static int GenerateZSeed(byte[] key)
        {
            if (key == null || key.Length <= 0)
            {
                return 0;
            }

            byte[] hash = ComputeHash(key, HashAlgorithmType.SHA512);
            if (hash.Length <= 4)
            {
                return BitConverter.ToInt32(hash, 0);
            }

            var random = new Random(BitConverter.ToInt32(hash, 0));
            byte[] values = new byte[4];
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = hash[random.Next(0, hash.Length)];
            }

            return BitConverter.ToInt32(values, 0);
        }

        private static int GetZOffset(Random random, int index)
        {
            int offset = ((index + 7) % ((random.Next(3, 219) + 5) >> 2));
            if (offset >= 255)
            {
                offset = 23;
            }

            return offset;
        }

        /// <summary>
        /// 简单加密数据
        /// </summary>
        /// <param name="inputData">要加密的数据</param>
        /// <param name="key">密钥</param>
        /// <returns>加密后的数据</returns>
        public static byte[] ZEncrypt(byte[] inputData, byte[] key)
        {
            var random = new Random(GenerateZSeed(key));
            var data = CompressEx.CompressBytes(inputData);
            byte[] result = new byte[data.Length];
            int offset = GetZOffset(random, 12);
            for (int i = 0; i < data.Length; i++)
            {
                result[i] = (byte)((data[i] - offset));
                offset += GetZOffset(random, i);
            }

            return CompressEx.CompressBytes(result);
        }

        /// <summary>
        /// 简单解密数据
        /// </summary>
        /// <param name="inputData">要解密的数据</param>
        /// <param name="key">密钥</param>
        /// <returns>解密后的数据</returns>
        public static byte[] ZDecrypt(byte[] inputData, byte[] key)
        {
            var data = CompressEx.DecompressBytes(inputData);
            var random = new Random(GenerateZSeed(key));
            byte[] result = new byte[data.Length];
            int offset = GetZOffset(random, 12);
            for (int i = 0; i < data.Length; i++)
            {
                result[i] = (byte)(data[i] + offset);
                offset += GetZOffset(random, i);
            }
            return CompressEx.DecompressBytes(result);
        }


        /// <summary>
        /// 简单加密数据
        /// </summary>
        /// <param name="srcDir">要加密的数据文件目录</param>
        /// <param name="searchPattern">文件搜索模式</param>
        /// <param name="searchOption">要压缩的文件搜索路径</param>
        /// <param name="outputFilePath">加密的数据文件路径</param>
        /// <param name="key">密钥</param>
        public static void ZEncrypt(string srcDir, string searchPattern, SearchOption searchOption, string outputFilePath, byte[] key)
        {
            if (!Directory.Exists(srcDir))
            {
                return;
            }

            string[] filePathArr = Directory.GetFiles(srcDir, searchPattern, searchOption);
            if (filePathArr.Length <= 0)
            {
                return;
            }

            DirectoryInfoEx.CheckFilePathDirectory(outputFilePath);
            using (var fs = new FileStream(outputFilePath, FileMode.CreateNew, FileAccess.Write))
            {
                var writer = new BinaryWriter(fs);
                writer.Write(key);
                writer.Write(filePathArr.Length);

                string fileName;
                byte[] buffer;

                foreach (string filePath in filePathArr)
                {
                    fileName = filePath.Substring(srcDir.Length);
                    buffer = ZEncrypt(Encoding.UTF8.GetBytes(fileName), key);
                    writer.Write(buffer.Length);
                    writer.Write(buffer);

                    buffer = ZEncrypt(File.ReadAllBytes(filePath), key);
                    writer.Write(buffer.Length);
                    writer.Write(buffer);
                }
            }

        }

        /// <summary>
        /// 简单解密数据
        /// </summary>
        /// <param name="srcFilePath">要解密的数据文件路径</param>
        /// <param name="outputDir">解密后数据文件目录</param>
        /// <param name="key">密钥</param>
        public static void ZDecrypt(string srcFilePath, string outputDir, byte[] key)
        {
            if (!File.Exists(srcFilePath))
            {
                return;
            }

            DirectoryInfoEx.CheckDirectory(outputDir);
            using (var fs = new FileStream(srcFilePath, FileMode.Open, FileAccess.Read))
            {
                fs.Position = key.Length;
                var reader = new BinaryReader(fs);
                int fileCount = reader.ReadInt32();
                int length;
                byte[] buffer;
                string fileName, dstFilePath;

                for (int i = 0; i < fileCount; i++)
                {
                    length = reader.ReadInt32();
                    buffer = reader.ReadBytes(length);
                    fileName = Encoding.UTF8.GetString(ZDecrypt(buffer, key));
                    dstFilePath = PathEx.Combine(PathType.Local, outputDir, fileName);
                    DirectoryInfoEx.CheckFilePathDirectory(dstFilePath);

                    length = reader.ReadInt32();
                    buffer = ZDecrypt(reader.ReadBytes(length), key);
                    File.WriteAllBytes(dstFilePath, buffer);
                }
            }
        }

        /// <summary>
        /// 生成Z密钥
        /// </summary>
        /// <param name="seed">种子值</param>
        /// <param name="bits">密钥位数,8的整数倍,非整数倍时余数忽略</param>
        /// <returns>密钥</returns>
        public static byte[] ZKey(long seed, int bits)
        {
            byte[] valueBytes = BitConverter.GetBytes(seed);
            byte[] hash = ComputeHash(valueBytes, HashAlgorithmType.SHA384);

            var random = new Random((int)(seed % int.MaxValue));
            byte[] key = new byte[bits / 8];
            for (int i = 0; i < key.Length; i++)
            {
                key[i] = hash[random.Next(0, hash.Length)];
            }

            return key;
        }
        #endregion

    }

    /// <summary>
    /// Hash算法类型
    /// </summary>
    public enum HashAlgorithmType
    {
        //Dotnet:     MD5 SHA1 SHA256 SHA384 SHA512  HMAC KeyedHashAlgorithm RIPEMD160
        //dotnetCore: MD5 SHA1 SHA256 SHA384 SHA512

        /// <summary>
        /// MD5
        /// </summary>
        MD5,

        /// <summary>
        /// SHA1
        /// </summary>
        SHA1,

        /// <summary>
        /// SHA256
        /// </summary>
        SHA256,

        /// <summary>
        /// SHA384
        /// </summary>
        SHA384,

        /// <summary>
        /// SHA512
        /// </summary>
        SHA512,

#if CORE

#else

        /// <summary>
        /// HMAC
        /// </summary>
        HMAC,

        /// <summary>
        /// KeyedHashAlgorithm
        /// </summary>
        KeyedHashAlgorithm,

        /// <summary>
        /// RIPEMD160
        /// </summary>
        RIPEMD160,

#endif

    }



    /// <summary>
    /// 对称加密算法类型
    /// </summary>
    public enum SymmetricAlgorithmType
    {
        /// <summary>
        /// Aes
        /// </summary>
        Aes,

        /// <summary>
        /// DES
        /// </summary>
        DES,

        /// <summary>
        /// RC2
        /// </summary>
        RC2,

        /// <summary>
        /// TripleDES
        /// </summary>
        TripleDES,

#if CORE

#else

        /// <summary>
        /// Rijndael
        /// </summary>
        Rijndael,

#endif
    }


    /// <summary>
    /// 非对称加密算法密钥对
    /// </summary>
    public class RSAKeys
    {
        /// <summary>
        /// 公钥
        /// </summary>
        public readonly string PublicKey;

        /// <summary>
        /// 私钥
        /// </summary>
        public readonly string PrivateKey;

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="publicKey">公钥</param>
        /// <param name="privateKey">私钥</param>
        public RSAKeys(string publicKey, string privateKey)
        {
            this.PublicKey = publicKey;
            this.PrivateKey = privateKey;
        }
    }

    /// <summary>
    /// 非对称加密算法类型
    /// </summary>
    public enum RSAAlgorithmType
    {
        /// <summary>
        /// 默认
        /// </summary>
        Default,

        /// <summary>
        /// RSACryptoServiceProvider
        /// </summary>
        RSACryptoServiceProvider,


#if !NET4_0

        /// <summary>
        /// RSACng-仅支持windows平台
        /// </summary>
        RSACng,

#endif

#if CORE

        /// <summary>
        /// RSABCrypt
        /// </summary>
        RSABCrypt,

        ///// <summary>
        ///// RSAOpenSsl-仅支持windows平台
        ///// </summary>
        //RSAOpenSsl

#else

#endif

    }



}
